package cn.ifafu.ifafu.util

import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit.SECONDS

private val client by lazy {
    OkHttpClient.Builder()
        .callTimeout(20, SECONDS)
        .connectTimeout(20, SECONDS)
        .readTimeout(20, SECONDS)
        .writeTimeout(20, SECONDS)
        .build()
}

private val JSON = "application/json; charset=utf-8".toMediaTypeOrNull()

fun doGet(
    url: String,
    query: Map<String, String> = emptyMap(),
    headers: Map<String, String> = emptyMap()
): Response {
    return try {
        Request.Builder()
            .url(makeUrlWithQuery(url, query))
            .get()
            .headers(makeHeaders(headers))
            .build()
            .execute()
    } catch (e: Exception) {
        Response(
            statusCode = 500,
            body = e.message ?: "未知错误",
            headers = emptyMap(),
            exception = e
        )
    }
}

fun doPostJson(
    url: String,
    json: String = "",
    headers: Map<String, String> = emptyMap()
): Response {
    return try {
        Request.Builder()
            .url(url)
            .post(json.toRequestBody(JSON))
            .headers(makeHeaders(headers))
            .build()
            .execute()
    } catch (e: Exception) {
        Response(
            statusCode = 500,
            body = e.message ?: "未知错误",
            headers = emptyMap(),
            exception = e
        )
    }
}

private fun makeUrlWithQuery(url: String, query: Map<String, String>): String {
    val httpUrlBuilder = (url + if (url.contains("?")) "&" else "?").toHttpUrl().newBuilder()
    query.forEach { (k, v) ->
        httpUrlBuilder.addQueryParameter(k, v)
    }
    return httpUrlBuilder.build().toString()
}

private fun makeHeaders(headers: Map<String, String>): Headers {
    val builder = Headers.Builder()
    val sno = DataUtils.getCurrentSno()
    if (sno != null) {
        builder.add("sno", sno)
    }
    headers.forEach { (k, v) ->
        builder.add(k, v)
    }
    return builder.build()
}

private fun Request.execute(): Response {
    val resp = client.newCall(this).execute()
    val body = resp.body.string()
    return Response(
        statusCode = resp.code,
        headers = resp.headers.toMap(),
        body = body,
    )
}

data class Response(
    val statusCode: Int,
    val body: String,
    val headers: Map<String, String>,
    val exception: Exception? = null
)